using Godot;
using System;

public partial class Cursor : Node2D
{
    #region 信号与处理
    [Signal]
    public delegate void OnPressedEventHandler(Vector2I pos);

    [Signal]
    public delegate void CameraMoveEndEventHandler();

    #endregion

    #region 子组件
    Sprite2D sprite2D;
    Area2D area2D;
    Camera2D camera2D;
    AnimationPlayer animationPlayer;
    Timer timer;
    #endregion

    void GetChildComponents()
    {
        sprite2D = GetNode<Sprite2D>("Sprite2D");
        area2D = GetNode<Area2D>("Sprite2D/Area2D");
        camera2D = GetNode<Camera2D>("Camera2D");
        animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
        timer = GetNode<Timer>("Timer");
        timer.Timeout += OnTimerTimeoutHandler;
    }

    static BaseLevelManager LevelManager => GameManager.Instance.LevelManager;
    static BaseMapManager MapManager => LevelManager.MapManager;

    public void Init(BaseLevelManager lv, BaseMapManager map)
    {
        var r = MapManager.GetUsedRect();
        var size = r.Size;
        Global.MapMaxX = size.X + r.Position.X;
        Global.MapMaxY = size.Y + r.Position.Y;

        camera2D.LimitLeft = r.Position.X * Global.TileMapGridSize;
        camera2D.LimitRight = Global.MapMaxX * Global.TileMapGridSize;
        camera2D.LimitTop = r.Position.Y * Global.TileMapGridSize;
        camera2D.LimitBottom = Global.MapMaxY * Global.TileMapGridSize;

        var midX = Mathf.CeilToInt(Global.MapMaxX / 2);
        var midY = Mathf.CeilToInt(Global.MapMaxY / 2);
        var pos = MapManager.MapToLocal(new Vector2I(midX, midY));
        sprite2D.Position = pos;
        camera2D.Position = pos;
    }

    public override void _Ready()
    {
        GetChildComponents();
    }

    static Vector2I ClampCursorPos(Vector2I v)
    {
        v.X = Mathf.Clamp(v.X, Global.MapMinX, Global.MapMaxX - Global.MapBorderWidth - 1);
        v.Y = Mathf.Clamp(v.Y, Global.MapMinY, Global.MapMaxY - Global.MapBorderWidth - 1);
        return v;
    }
    Vector2 ClampCursorPos(Vector2 v)
    {
        v.X = Mathf.Clamp(v.X, camera2D.LimitLeft + Global.MapBorderWidth * Global.TileMapGridSize, camera2D.LimitRight - (Global.MapBorderWidth + 1) * Global.TileMapGridSize);
        v.Y = Mathf.Clamp(v.Y, camera2D.LimitTop + Global.MapBorderWidth * Global.TileMapGridSize, camera2D.LimitBottom - (Global.MapBorderWidth + 1) * Global.TileMapGridSize);
        return v;
    }

    public void LookAt(Robot robot)
    {
        sprite2D.Position = robot.Position;
        camera2D.Position = robot.Position;
    }

    public void FocusTarget(Node2D target, bool imediate = false)
    {
        if (imediate)
        {
            camera2D.Position = target.Position;
        }
        else
        {
            GameManager.Instance.LockInput();
            MoveTo(target.Position);
        }
        animationPlayer.Play("RESET");
    }
    static readonly float minMoveTime = 0.09f;
    static readonly float maxMoveTime = 1.5f;
    static float ClampMoveTime(float val)
    {
        return Mathf.Max(Mathf.Min(val, maxMoveTime), minMoveTime);
    }

    private void OnTimerTimeoutHandler()
    {
        EmitSignal(SignalName.CameraMoveEnd);
        GameManager.Instance.AllowInput();
    }

    private void OnTweenFinished()
    {
        IsMoving = false;
        animationPlayer.Play("blink");
        timer.Start();
    }
    public float Speed = 0.003f;
    Tween tween;

    public static readonly int TO_LONG_DISTANCE = 12;

    public void MoveTo(Vector2 tarPos)
    {
        tween?.Stop();
        tween = CreateTween();
        tween.Finished += OnTweenFinished;
        IsMoving = true;
        var timeSpan = 0f;
        camera2D.Position = sprite2D.Position;
        // 如果两点间没有任何一个轴相同
        if (tarPos.X != Position.X && tarPos.Y != camera2D.Position.Y)
        {
            // 计算 cursor 和 tarPos 之间的转折点
            var turn = new Vector2(tarPos.X, camera2D.Position.Y);
            var dis = camera2D.Position.DistanceTo(turn);
            timeSpan = ClampMoveTime(dis * Speed);
            tween.TweenProperty(camera2D, "position", turn, timeSpan);
            tween.Parallel().TweenProperty(sprite2D, "position", turn, timeSpan);
            dis = turn.DistanceTo(tarPos);
            timeSpan = ClampMoveTime(dis * Speed);
            tween.TweenProperty(camera2D, "position", tarPos, timeSpan);
            tween.Parallel().TweenProperty(sprite2D, "position", tarPos, timeSpan);
        }
        else
        {
            // 没有转折点则直接移动一次就行了
            var dis = camera2D.Position.DistanceTo(tarPos);
            timeSpan = ClampMoveTime(dis * Speed);
            tween.TweenProperty(camera2D, "position", tarPos, timeSpan);
            tween.Parallel().TweenProperty(sprite2D, "position", tarPos, timeSpan);
        }
    }


    #region 鼠标拖拽移动镜头
    public bool IsDraging { get; private set; }
    public Vector2 CurSorPosition { get => sprite2D.Position; }
    Vector2 dragStartPos;
    Vector2 lastPos;
    bool IsMoving;

    public override void _UnhandledInput(InputEvent @event)
    {
        if (GameManager.Instance.IsLockInput) return;
        if (IsMoving || MapManager == null) return;
        if (@event is InputEventMouseButton button && button.ButtonIndex == MouseButton.Left)
        {
            if (button.Pressed)
            {
                dragStartPos = button.Position;
            }
            else if (!button.Pressed)
            {
                if (!IsDraging)
                {
                    if (!GameManager.Instance.IsLockInput)
                    {
                        var v = MapManager.LocalToMap(GetGlobalMousePosition());
                        v = ClampCursorPos(v);
                        var tarPos = MapManager.MapToLocal(v);
                        sprite2D.Position = tarPos;
                        EmitSignal(SignalName.OnPressed, v);
                        animationPlayer.Play("RESET");
                    }
                }
                // 只要松开就一定是拖拽结束
                IsDraging = false;
            }

        }

        if (@event is InputEventMouseMotion motion)
        {
            if (motion.ButtonMask == MouseButtonMask.Left)
            {
                var dis = motion.Position - dragStartPos;
                if (dis.Length() > 10)
                {
                    IsDraging = true;
                    lastPos = camera2D.Position - motion.ScreenRelative;
                    lastPos = ClampCursorPos(lastPos);
                    camera2D.Position = lastPos;
                }
            }
        }
    }
    #endregion
}
